from typing import Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, delete, text
from fastapi import HTTPException, status
from datetime import datetime

from modules.system.models.user import User
from modules.system.models.role import Role
from modules.system.models.user_role import UserRole
from modules.system.models.dept import Department
from modules.system.schemas.user_schemas import UserCreate, UserUpdate, UserCreateDB, UserUpdateDB
from core.security import pwd_context
from modules.system.params.user_params import UserQuery
from core.permission.decorators import with_permission
from utils.query_builder import build_select_stmt
from modules.system.curd.user_curd import user_dao
from modules.system.curd.role_curd import role_dao
from core.pagination import paging_data, _CustomPageParams
from core.pagination import paging_data, _CustomPageParams
from utils.log_util import logger


class UserService:
    @staticmethod
    async def get_user_by_id(db: AsyncSession, user_id: int) -> Optional[User]:
        return await user_dao.get_by_id(db, user_id)

    @staticmethod
    async def get_user_by_username(db: AsyncSession, username: str) -> Optional[User]:
        return await user_dao.get_by_username(db, username)

    @staticmethod
    async def get_user_profile_by_id(db: AsyncSession, user_id: int) -> Optional[Dict[str, Any]]:
        """根据用户ID获取用户资料，返回字典结构以便路由层 MeResponse 使用"""
        user = await user_dao.get_by_id(db, user_id)
        if not user:
            return None
        # 查询用户的角色ID列表（使用角色仓储方法）
        role_ids = await role_dao.get_role_ids_by_user_id(db, user_id)
        return {
            "id": getattr(user, "id", None),
            "username": getattr(user, "username", None),
            "name": getattr(user, "name", None),
            "avatar": getattr(user, "avatar", None),
            "email": getattr(user, "email", None),
            "phone": getattr(user, "phone", None),
            "department": getattr(user, "department", None),
            "roles": role_ids,
            "person_id": getattr(user, "person_id", None),
        }
        
    @staticmethod
    @with_permission(User.__tablename__)
    async def get_users_with_pagination(
        db: AsyncSession,
        query_params: UserQuery,
        permission_condition: Optional[str] = None
    ) -> dict:
        """获取用户列表（分页），参考部门服务复用通用查询构建器"""
        # 使用通用查询构建器生成基础筛选语句
        stmt = build_select_stmt(
            User,
            query_params,
            text_like_fields={"username", "name", "email", "phone", "department"},
            keyword_fields={"username", "name", "email", "phone"},
        )
        # 权限条件（如果存在且不是默认 1=1）
        if permission_condition and permission_condition != "1=1":
            stmt = stmt.where(text(permission_condition))

        # 关联部门，补充部门名称列
        stmt = (
            stmt.add_columns(Department.dept_name)
                .join(Department, User.department == Department.dept_code, isouter=True)
        )

        # 分页与排序：统一使用分页工具
        stmt = stmt.order_by(User.id.asc())
        params = _CustomPageParams(page=query_params.page, size=query_params.size)
        page_data = await paging_data(db, stmt, params=params)
        rows = page_data["items"]

        # 构建返回数据，批量查询角色并映射
        items: list[dict] = []
        if rows:
            user_ids = [row[0].id for row in rows]
            user_roles_map = await role_dao.get_role_ids_map_by_user_ids(db, user_ids)

            for user, dept_name in rows:
                items.append({
                    'id': user.id,
                    'username': user.username,
                    'name': user.name,
                    'email': user.email,
                    'phone': user.phone,
                    'department': user.department,
                    'person_id': user.person_id,
                    'status': user.status,
                    'create_time': user.create_time,
                    'update_time': user.update_time,
                    'dept_name': dept_name,
                    'roles': user_roles_map.get(user.id, [])
                })

        # 复用分页封装：就地替换 items，并对齐 pages 字段
        page_data["items"] = items
        page_data["pages"] = page_data.get("total_pages", 0)
        page_data.pop("links", None)
        return page_data

    @staticmethod
    async def create_user(db: AsyncSession, payload: UserCreate) -> User:
        # 唯一性检查
        if await UserService.get_user_by_username(db, payload.username):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="用户名已存在")
        if payload.email:
            res = await db.execute(select(User).where(User.email == payload.email))
            if res.scalar_one_or_none():
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="邮箱已存在")
        # 通过仓储创建，先对密码进行哈希处理
        hashed = pwd_context.hash(payload.password)
        # 构造新的数据对象用于持久化（避免明文密码入库）
        create_data = UserCreateDB(
            username=payload.username,
            password=hashed,
            name=payload.name,
            avatar=payload.avatar,
            email=payload.email,
            phone=payload.phone,
            status=payload.status if payload.status is not None else 1,
            department=payload.department,
            person_id=payload.person_id
        )
        db_user = await user_dao.create(db, create_data)
        await db.commit()
        await db.refresh(db_user)

        # 处理角色关联（如果提供）
        if payload.roles:
            role_ids = sorted({rid for rid in payload.roles if isinstance(rid, int) and rid > 0})
            if role_ids:
                existing_roles = await db.execute(select(Role.id).where(Role.id.in_(role_ids)))
                valid_role_ids = {row[0] for row in existing_roles.all()}
                for rid in valid_role_ids:
                    db.add(UserRole(user_id=db_user.id, role_id=rid))
                await db.commit()

        return db_user

    @staticmethod
    async def update_user(db: AsyncSession, user_id: int, payload: UserUpdate) -> User:
        db_user = await UserService.get_user_by_id(db, user_id)
        if not db_user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
        update_data = payload.model_dump(exclude_unset=True)
        # 唯一性检查
        if "username" in update_data and update_data["username"] != db_user.username:
            if await UserService.get_user_by_username(db, update_data["username"]):
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="用户名已存在")
        # 使用仓储更新基本字段：构造仅包含 ORM 字段的更新模型
        base_update = payload.model_dump(exclude_unset=True, exclude_none=True)
        base_update.pop("roles", None)
        update_db = UserUpdateDB(**base_update)
        db_user = await user_dao.update(db, user_id, update_db)
        # 处理角色关联（如果提供）
        if "roles" in update_data:
            role_ids = sorted({rid for rid in update_data["roles"] if isinstance(rid, int) and rid > 0})
            # 先删除该用户的所有角色关联
            await db.execute(delete(UserRole).where(UserRole.user_id == user_id))
            
            if role_ids:
                # 验证角色是否存在
                existing_roles = await db.execute(select(Role.id).where(Role.id.in_(role_ids)))
                valid_role_ids = {row[0] for row in existing_roles.all()}
                # 添加新的角色关联
                for rid in valid_role_ids:
                    db.add(UserRole(user_id=db_user.id, role_id=rid))
                    
        await db.commit()
        await db.refresh(db_user)

        return db_user

    @staticmethod
    async def delete_user(db: AsyncSession, user_id: int) -> None:
        db_user = await UserService.get_user_by_id(db, user_id)
        if not db_user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
        await user_dao.delete(db, user_id)
        await db.commit()

    @staticmethod
    async def change_password(db: AsyncSession, user_id: int, old_password: str, new_password: str) -> None:
        db_user = await UserService.get_user_by_id(db, user_id)
        if not db_user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
        if not pwd_context.verify(old_password, db_user.password):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="旧密码不正确")
        db_user.password = pwd_context.hash(new_password)
        db_user.update_time = datetime.now()
        await db.commit()